package net.easyunion.dataservice.common.tool;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 缓存
 * @author lph
 * @version 创建时间：2012年9月22日  上午10:42:28
 */
public class LruCache<K, E> {

	private final String name;
	int maxSize = 1000;
	private boolean resizing = false;
	private boolean adaptive = false;

	@SuppressWarnings("serial")
	private Map<K, E> cache = new LinkedHashMap<K, E>(2000, 0.75f, true) {
		protected boolean removeEldestEntry(Map.Entry<K, E> eldest) {
			// synchronization miss with old value on maxSize here is ok
			if (super.size() > maxSize) {
				// if (isAdaptive() && !isResizing()) {
				//
				// } else {
				// super.remove(eldest.getKey());
				// elementCleaned(eldest.getValue());
				// }

				return true;
			}
			return false;
		}
	};

	// public void setMaxSize( int maxSize )
	// {
	// this.maxSize = maxSize;
	// }

	/**
	 * Creates a LRU cache. If <CODE>maxSize < 1</CODE> an
	 * IllegalArgumentException is thrown.
	 * 
	 * @param name
	 *            name of cache
	 * @param maxSize
	 *            maximum size of this cache
	 * @param cacheManager
	 *            adaptive cache manager or null if adaptive caching not needed
	 */
	public LruCache(String name, int maxSize) {

		if (name == null || maxSize < 1) {
			throw new IllegalArgumentException("maxSize=" + maxSize + ", name=" + name);
		}
		this.name = name;
		this.maxSize = maxSize;
	}

	public String getName() {
		return this.name;
	}

	public synchronized void put(K key, E element) {
		if (key == null || element == null) {
			throw new IllegalArgumentException("key=" + key + ", element=" + element);
		}
		cache.put(key, element);
	}

	public synchronized E remove(K key) {
		if (key == null) {
			throw new IllegalArgumentException("Null parameter");
		}
		return cache.remove(key);
	}

	public synchronized E get(K key) {
		if (key == null) {
			throw new IllegalArgumentException();
		}
		return cache.get(key);
	}

	public synchronized void clear() {
		resizeInternal(0);
	}

	public synchronized int size() {
		return cache.size();
	}

	/**
	 * Returns the maximum size of this cache.
	 * 
	 * @return maximum size
	 */
	public int maxSize() {
		return maxSize;
	}

	/**
	 * Changes the max size of the cache. If <CODE>newMaxSize</CODE> is greater
	 * then <CODE>maxSize()</CODE> next invoke to <CODE>maxSize()</CODE> will
	 * return <CODE>newMaxSize</CODE> and the entries in cache will not be
	 * modified.
	 * <p>
	 * If <CODE>newMaxSize</CODE> is less then <CODE>size()</CODE> the cache
	 * will shrink itself removing least recently used element until
	 * <CODE>size()</CODE> equals <CODE>newMaxSize</CODE>. For each element
	 * removed the {@link #elementCleaned} method is invoked.
	 * <p>
	 * If <CODE>newMaxSize</CODE> is less then <CODE>1</CODE> an
	 * {@link IllegalArgumentException} is thrown.
	 * 
	 * @param newMaxSize
	 *            the new maximum size of the cache
	 */
	public synchronized void resize(int newMaxSize) {
		if (newMaxSize < 1) {
			throw new IllegalArgumentException("newMaxSize=" + newMaxSize);
		}
		resizeInternal(newMaxSize);
	}

	private void resizeInternal(int newMaxSize) {
		resizing = true;
		try {
			if (newMaxSize >= size()) {
				maxSize = newMaxSize;
			} else if (newMaxSize == 0) {
				cache.clear();
			} else {
				maxSize = newMaxSize;
				java.util.Iterator<Map.Entry<K, E>> itr = cache.entrySet().iterator();

				while (itr.hasNext() && cache.size() > maxSize) {
					E element = itr.next().getValue();
					itr.remove();
					elementCleaned(element);
				}
			}
		} finally {
			resizing = false;
		}
	}

	boolean isResizing() {
		return resizing;
	}

	public void elementCleaned(E element) {
	}

	public boolean isAdaptive() {
		return adaptive;
	}

	public void setAdaptiveStatus(boolean status) {
		this.adaptive = status;
	}

	public void putAll(Map<K, E> map) {
		cache.putAll(map);
	}

}
